Rust 2024 Edition: qué cambió con Rust 1.85 y por qué importa

Rust publica una nueva versión del compilador cada seis semanas. Eso está bien para correcciones y funciones menores, pero hay cambios que rompen la compatibilidad semántica aunque el código antiguo siga compilando. Para eso existen las Editions: puntos de inflexión donde el lenguaje puede evolucionar sin dejar atrás a nadie.

La 2015 fue la original. La 2018 fue la que trajo async/await como mecanismo central del lenguaje. La 2021 mejoró el manejo de closures, simplificó los imports y pulió los format strings. Y con Rust 1.85, publicado en febrero de 2025, llegó la 2024 Edition.

Para activarla en un proyecto existente basta con cambiar una línea en Cargo.toml:

[package]
edition = "2024"

Y si quieres que el compilador migre el código automáticamente, tienes cargo fix --edition. En la mayoría de proyectos funciona sin intervención manual.

Un apunte que vale la pena repetir: las Editions son compatibles hacia atrás. Tu código de Rust 2021 sigue compilando. Las crates de distintas Editions pueden convivir en el mismo proyecto. No hay prisa para migrar, y no hay fecha de fin de soporte para las ediciones anteriores.

Async closures: el cambio más esperado

Si llevas tiempo con Rust asíncrono, sabes lo molesto que era intentar pasar una closure async como argumento. No era posible de forma directa. Necesitabas un wrapper, una función separada, o lucharte con los tipos hasta conseguir algo que compilara.

A partir de la 2024 Edition eso se acabó:

let fetch = async |url: &str| {
    reqwest::get(url).await
};

Tan sencillo como eso. Y más importante aún: cuando escribes una función que acepta un callback asíncrono, el tipo del parámetro ya tiene sentido:

async fn run_handler<F>(handler: F, url: &str)
where
    F: AsyncFn(&str) -> reqwest::Response,
{
    handler(url).await;
}

Antes, conseguir algo equivalente requería pensar mucho en qué combinación de traits usar. Ahora el compilador entiende qué quieres decir.

Esto tiene impacto directo en frameworks web y sistemas de middleware. Cualquier librería que acepte handlers, filtros o transformaciones asíncronas puede ofrecer una API mucho más limpia.

Bloques gen: iteradores sin tanto trabajo

Implementar un iterador personalizado en Rust siempre ha sido verboso. Defines una struct, implementas Iterator para ella, escribes fn next(&mut self) -> Option<Self::Item> y manejas el estado interno a mano. No es difícil, pero es mucho código para algo conceptualmente sencillo.

Los bloques gen cambian eso:

let pares = gen {
    for i in 0..10 {
        yield i * 2;
    }
};

for n in pares {
    println!("{n}");
}

El bloque produce valores con yield y el compilador genera el iterador por ti. Sin struct, sin implementar traits manualmente, sin gestionar el estado.

Hay que ser honesto sobre el estado actual: gen blocks están en nightly y requieren #![feature(gen_blocks)]. Están en camino a stable, pero todavía no han llegado. Puedes usarlos ya si trabajas con nightly, y es razonable esperarlos en stable durante 2025.

Para quien conoce Python, la analogía con los generadores es directa. Para quien viene de C#, es similar a los métodos con yield return. La diferencia es que en Rust tienes las mismas garantías de seguridad de siempre.

Nuevas reglas de lifetime en closures

Las closures en versiones anteriores de Rust tenían un comportamiento a veces sorprendente con las referencias. El compilador no siempre podía inferir correctamente los lifetimes de las referencias capturadas, lo que obligaba a añadir anotaciones en sitios donde no hacía falta.

La 2024 Edition relaja esas reglas en los casos más comunes. Concretamente, mejora la inferencia cuando una closure captura referencias mutables y las pasa a otras funciones. El resultado es que el código cotidiano necesita menos anotaciones de lifetime, y cuando sí hay un problema real el compilador da mensajes de error más precisos.

No es el cambio más espectacular de la Edition, pero sí uno de los más agradables en el día a día. Menos 'a y 'b que justificar cuando el compilador ya podría deducirlos solo.

Sintaxis use<'a> para captura precisa de lifetimes

Cuando devuelves impl Trait desde una función, el compilador tiene que saber qué lifetimes captura ese tipo opaco. Antes, asumía que capturabas todos los lifetimes del ámbito, lo que generaba errores en casos donde en realidad solo necesitabas uno concreto.

La sintaxis use<> te permite ser explícito:

fn procesar<'a>(texto: &'a str) -> impl Display + use<'a> {
    // solo captura 'a, nada más
}

Sin esta anotación, el compilador podría quejarse de lifetimes que en realidad no estás usando. Con ella, el contrato queda claro: "este tipo opaco captura exactamente este lifetime y nada más".

Es una mejora especialmente relevante en código de librería donde los tipos de retorno son parte de la API pública. Menos errores confusos para los usuarios de tu crate.

Unsafe extern: más claridad en el código FFI

En Rust 2024, declarar funciones externas de C requiere unsafe extern:

unsafe extern "C" {
    fn strlen(s: *const i8) -> usize;
}

Antes bastaba con extern "C" { ... }. El cambio puede parecer burocrático, pero tiene su lógica: declarar que una función FFI existe es en sí mismo asumir un contrato de seguridad. Estás diciendo que la firma que escribes es correcta, que la función existe en la librería enlazada, que los tipos coinciden. Si alguna de esas cosas es falsa, el comportamiento es indefinido.

Marcar el bloque como unsafe hace visible ese compromiso. No afecta a cómo llamas las funciones después (que ya era unsafe), solo a la declaración.

Y la buena noticia: cargo fix --edition migra estos bloques automáticamente. No tienes que buscarlos a mano.

Cómo migrar hoy

Si tienes un proyecto en Rust 2021 y quieres pasarte a la 2024 Edition, el proceso es directo:

  • Ejecuta cargo fix --edition 2024 en la raíz del proyecto.
  • Revisa cualquier bloque extern que tengas: el fixer los migra, pero merece un vistazo.
  • Cambia edition = "2021" a edition = "2024" en Cargo.toml.
  • Compila y ejecuta tus tests.

En la mayoría de proyectos medianos no hace falta ningún cambio manual. Las breaking changes de la 2024 Edition son deliberadamente pocas y el tooling las resuelve solo.

Si tu proyecto depende de crates que todavía no han adoptado la 2024 Edition, no hay problema: las crates de distintas ediciones conviven sin fricción. Tú puedes migrar tu código sin esperar a que lo hagan tus dependencias.

Para profundizar en cómo encaja esta Edition con la evolución reciente del lenguaje, puedes leer sobre las últimas novedades de Rust 1.95 o repasar el contexto más amplio de Rust en 2025: tendencias y adopción.

Imagen: Pexels / Markus Winkler

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP